/* * Copyright 2013 FortSoft * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with * the License. You may obtain a copy of the License in the LICENSE file, or at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package ro.fortsoft.wicket.jade; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Map; import org.apache.wicket.MarkupContainer; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.IMarkupCacheKeyProvider; import org.apache.wicket.markup.IMarkupResourceStreamProvider; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.markup.html.panel.GenericPanel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.StringResourceStream; import org.apache.wicket.util.string.Strings; import de.neuland.jade4j.Jade4J; import de.neuland.jade4j.parser.Parser; import de.neuland.jade4j.parser.node.Node; import de.neuland.jade4j.template.JadeTemplate; import de.neuland.jade4j.template.ReaderTemplateLoader; import de.neuland.jade4j.template.TemplateLoader; /** * @author Decebal Suiu */ public abstract class JadePanel extends GenericPanel<Map<String, Object>> implements IMarkupResourceStreamProvider, IMarkupCacheKeyProvider { private static final long serialVersionUID = 1L; private boolean throwJadeExceptions; private transient String htmlMarkup; private transient String stackTraceAsString; public JadePanel(String id, Map<String, Object> values) { this(id, Model.ofMap(values)); } public JadePanel(String id, IModel<Map<String, Object>> model) { super(id, model); } @Override public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass) { // load the jade template JadeTemplate template = null; try { template = getTemplate(containerClass); } catch (IOException e) { // throw new WicketRuntimeException(e); onException(e); } // evaluate the jade template if (htmlMarkup == null) { htmlMarkup = Jade4J.render(template, getModelObject()); } // create the markup (string) StringBuffer buffer = new StringBuffer(); buffer.append("<wicket:panel>"); buffer.append(htmlMarkup); buffer.append("</wicket:panel>"); return new StringResourceStream(buffer.toString()); } @Override public String getCacheKey(MarkupContainer container, Class<?> containerClass) { // don't cache the evaluated template return null; } @Override public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) { if (!Strings.isEmpty(stackTraceAsString)) { // TODO: only display the Jade error/stacktrace in development mode? replaceComponentTagBody(markupStream, openTag, Strings.toMultilineMarkup(stackTraceAsString)); } else { if (htmlMarkup == null) { // initialize htmlMarkup getMarkupResourceStream(null, null); } replaceComponentTagBody(markupStream, openTag, htmlMarkup); } } /** * Whether any Jade exception should be trapped and displayed on the panel (false) or thrown * up to be handled by the exception mechanism of Wicket (true). The default is false, which * traps and displays any exception without having consequences for the other components on the * page. * <p> * Trapping these exceptions without disturbing the other components is especially useful in CMS * like applications, where 'normal' users are allowed to do basic scripting. On errors, you * want them to be able to have them correct them while the rest of the application keeps on * working. * </p> * * @return Whether any Jade exceptions should be thrown or trapped. The default is false. */ public JadePanel setThrowJadeExceptions(boolean value) { this.throwJadeExceptions = value; return this; } protected JadeTemplate getTemplate(Class<?> containerClass) throws IOException { String templateName = containerClass.getName(); String resourceName = containerClass.getSimpleName() + ".jade"; InputStream inputStream = containerClass.getResourceAsStream(resourceName); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); JadeTemplate template = new JadeTemplate(); TemplateLoader templateLoader = new ReaderTemplateLoader(reader, templateName); Parser parser = new Parser(templateName, templateLoader); Node root = parser.parse(); template.setTemplateLoader(templateLoader); template.setRootNode(root); template.setPrettyPrint(true); return template; } @Override protected void onDetach() { super.onDetach(); htmlMarkup = null; stackTraceAsString = null; } protected void onException(Exception e) { if (!throwJadeExceptions) { // print the exception on the panel stackTraceAsString = Strings.toString(e); } else { // rethrow the exception throw new WicketRuntimeException(e); } } }